package com.pig4cloud.pigx.strategy.executor.contract.task.supporter;

import com.pig4cloud.pigx.common.customize.contants.BigDecimalConstants;
import com.pig4cloud.pigx.common.customize.utils.BigDecimalUtils;
import com.pig4cloud.pigx.strategy.enums.StrategyTaskContractEnum;
import com.pig4cloud.pigx.strategy.util.StrategyContractUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;

/**
 * 策略-合约计算器
 *
 * @since 2022/11/28 20:03
 */
public class StrategyTaskContractCalculator {

    /**
     * 价格回调比例
     * <p> 价格回调比例 = （最高波动比例 - 当前波动比例） / （最高波动比例 + 100） * 100
     * <p> 上涨趋势时下跌，回调比例为正数。下跌趋势时上涨，回调比例为负。因此下跌时乘于-1，使价格回调时回调比例统一为正数
     *
     * @param currentFluctuationRatio 当前波动比例
     * @param highestFluctuationRatio 最高波动比例
     * @return {@link BigDecimal}
     * @since 2022/11/29 14:16
     */
    public static BigDecimal callbackRatio(BigDecimal currentFluctuationRatio,
                                           BigDecimal highestFluctuationRatio) {
        // 根据最高价格波动比例判断价格趋势，添加变量，使价格回调时回调比例统一为正数
        BigDecimal variable = highestFluctuationRatio.compareTo(BigDecimal.ZERO) > 0 ?
                BigDecimal.ONE
                : BigDecimal.ONE.negate();

        return highestFluctuationRatio.subtract(currentFluctuationRatio)
                .divide(highestFluctuationRatio.add(BigDecimalConstants.HUNDRED), 4, RoundingMode.HALF_UP)
                .multiply(BigDecimalConstants.HUNDRED)
                .multiply(variable);
    }

    /**
     * 利润回降比例
     * <p> 最多超出止盈比例部分盈利 = 最高利润率 - 止盈比例
     * <p> 当前超出止盈比例部分盈利 = 当前利润率 - 止盈比例
     * <p> 利润回降比例 = （1 -（当前超出止盈比例部分盈利 / 最多超出止盈比例部分盈利））* 100
     * <p> 调用该方法时任务一定是曾经盈利过的，即最高盈利大于0，且最高利润率大于止盈比例
     *
     * @param profitRate        当前利润率
     * @param highestProfitRate 最高利润率
     * @param stopProfitRatio   止盈比例
     * @return {@link BigDecimal}
     * @since 2022/11/29 14:16
     */
    public static BigDecimal fallbackRatio(BigDecimal profitRate,
                                           BigDecimal highestProfitRate,
                                           BigDecimal stopProfitRatio) {

        // 最多超出止盈比例部分盈利
        BigDecimal highestExtraProfitRate = highestProfitRate.subtract(stopProfitRatio);
        // 当前超出止盈比例部分盈利
        BigDecimal currentExtraProfitRate = profitRate.subtract(stopProfitRatio);

        return BigDecimal.ONE.subtract(currentExtraProfitRate.divide(highestExtraProfitRate, 4, RoundingMode.HALF_UP))
                .movePointRight(2);

    }

    /**
     * 当前价格波动比例
     * <p> 当前价格波动比例 = （价格 - 上次开仓均价） / 上次开仓均价 * 100
     * <p> 上涨为正数，下跌为负数
     *
     * @param price       当前价格
     * @param lastOpenAvg 上次开仓均价
     * @return {@link BigDecimal}
     * @since 2022/11/29 14:16
     */
    public static BigDecimal currentFluctuationRatio(BigDecimal price, BigDecimal lastOpenAvg) {

        return price.subtract(lastOpenAvg)
                .divide(lastOpenAvg, 4, RoundingMode.HALF_UP)
                .movePointRight(2);

    }

    /**
     * 价格波动比例
     *
     * @param fluctuationRatio    价格波动比例
     * @param fluctuationIncrease 价格波动增幅
     * @param addCount            补仓次数
     * @return {@link BigDecimal}
     * @since 2022/11/29 16:07
     */
    public static BigDecimal fluctuationRatio(BigDecimal fluctuationRatio, BigDecimal fluctuationIncrease, Integer addCount) {

        return fluctuationRatio.add(fluctuationIncrease.multiply(BigDecimal.valueOf(addCount)));

    }

    /**
     * 补仓数量
     * <p> 补仓数量 = 持仓数量 * 补仓比例
     *
     * @param nowNum    持仓数量
     * @param lockRatio 补仓比例
     * @return {@link int}
     * @since 2022/11/29 16:07
     */
    public static int lockNum(int nowNum, BigDecimal lockRatio) {

        BigDecimal totalNum = BigDecimal.valueOf(nowNum);
        return totalNum.multiply(BigDecimalUtils.toPercent(lockRatio))
                .setScale(0, RoundingMode.HALF_UP)
                .intValue();

    }

    /**
     * 利润结算
     * <p> 利润结算 = 盈利 * 平台分成比例（%）
     *
     * @param profit        盈利
     * @param platformRatio 平台分成比例（%）
     * @return {@link BigDecimal}
     * @since 2022/11/29 16:07
     */
    public static BigDecimal profitSettlement(BigDecimal profit, BigDecimal platformRatio) {

        return profit.multiply(BigDecimalUtils.toPercent(platformRatio));

    }

    /**
     * 计算下次补仓价格
     * <p> 首次补仓方向为多：上次成交价 * （1 - 价格波动比例）
     * <p> 首次补仓方向为空：上次成交价 * （1 + 价格波动比例）
     * <p> 没有补过仓，根据当前价格和上次成交价的比较结果进行计算。即高于上次成交价时，按空算，低于上次成交价时，按多算
     *
     * @param firstAddSide     首次补仓方向（0做多、1做空、2尚未补仓）
     * @param price            当前价格
     * @param lastAvgOpen      上次成交价
     * @param fluctuationRatio 价格波动比例（%）
     * @return {@link BigDecimal}
     * @since 2022/11/29 16:07
     */
    public static BigDecimal nextAddPrice(String firstAddSide,
                                          BigDecimal price,
                                          BigDecimal lastAvgOpen,
                                          BigDecimal fluctuationRatio) {

        // 是尚未补仓
        if (Objects.equals(StrategyTaskContractEnum.FirstAddSide.NONE.getValue(), firstAddSide)) {
            firstAddSide = price.compareTo(lastAvgOpen) >= 0 ?
                    StrategyTaskContractEnum.FirstAddSide.SHORT.getValue()
                    : StrategyTaskContractEnum.FirstAddSide.LONG.getValue();
        }

        // 多
        if (Objects.equals(StrategyTaskContractEnum.FirstAddSide.LONG.getValue(), firstAddSide)) {
            return lastAvgOpen.multiply(BigDecimal.ONE.subtract(BigDecimalUtils.toPercent(fluctuationRatio)));
        } else {
            // 空
            return lastAvgOpen.multiply(BigDecimal.ONE.add(BigDecimalUtils.toPercent(fluctuationRatio)));
        }

    }

    /**
     * 正向止盈价格回调比例
     * <p> 做多：（当前价格 - 最低价格） / 最低价格 * 100
     * <p> 做空：（最高价格 - 当前价格） / 最高价格 * 100
     *
     * @param side         方向
     * @param currentPrice 当前价格
     * @param lowestPrice  最低价格
     * @param highestPrice 最高价格
     * @return {@link BigDecimal}
     * @since 2022/11/29 14:16
     */
    public static BigDecimal forwardStopCallbackRatio(String side,
                                                      BigDecimal currentPrice,
                                                      BigDecimal lowestPrice,
                                                      BigDecimal highestPrice) {
        if (BigDecimal.ZERO.compareTo(lowestPrice) == 0
                || BigDecimal.ZERO.compareTo(highestPrice) == 0) {
            return BigDecimal.ZERO;
        }

        if (StrategyContractUtil.isLong(side)) {
            return currentPrice.subtract(lowestPrice)
                    .divide(lowestPrice, 4, RoundingMode.HALF_UP)
                    .movePointRight(2);
        } else {
            return highestPrice.subtract(currentPrice)
                    .divide(highestPrice, 4, RoundingMode.HALF_UP)
                    .movePointRight(2);
        }
    }

}
